#pragma once

#include <iostream>
#include <unordered_map>
#include <memory>
#include <functional>

#include "NoCopy.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "Comm.hpp"

//封装事件并设置ET模式
uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);

static const int size = 128;

class Connection;
class Server;

using func_t = std::function<void(std::weak_ptr<Connection>)>;

class Connection
{
public:
    //构造
    Connection(int fd)
    : _sockfd(fd)
    {}

    //设置事件处理方法
    void SetHandler(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    //设置ip端口
    void SetIpPort(std::string ip, uint16_t port)
    {
        _client_ip = ip;
        _client_port = port;
    }
    //设置服务器指针
    void SetWeakPtr(std::weak_ptr<Server> server_ptr)
    {
        _server_ptr = server_ptr;
    }

    //追加接收缓冲区
    void AppendInbuffer(const std::string& data)
    {
        _inbuffer += data;
    }

    //获取异常处理方法
    func_t GetExceptCb()
    {
        return _except_cb;
    }
    //获取读事件处理方法
    func_t GetRecvCb()
    {
        return _recv_cb;
    }
    //获取写事件处理方法
    func_t GetSendCb()
    {
        return _send_cb;
    }
    //获取fd
    int GetFd()
    {
        return _sockfd;
    }
    //获取客户ip端口
    std::string GetIp()
    {
        return _client_ip;
    }
    uint16_t GetPort()
    {
        return _client_port;
    }
    //获取发送缓冲区数据
    std::string& GetOutBuffer()
    {
        return _outbuffer;
    }
    //获取接收缓冲区数据
    std::string& GetInBuffer()
    {
        return _inbuffer;
    }
private:
    func_t _recv_cb; //读事件处理方法
    func_t _send_cb; //写事件处理方法
    func_t _except_cb; //异常事件处理方法

    std::weak_ptr<Server> _server_ptr; //服务器指针

    std::string _client_ip; //客户ip
    uint16_t _client_port; //客户端口
    int _sockfd; //fd

    std::string _inbuffer; //接收缓冲区
    std::string _outbuffer; //发送缓冲区
};

class Server : public NoCopy, std::enable_shared_from_this<Server>
{
    static const int num = 64;

private:
    //设置读写事件是否关心
    void EnableEvent(int fd, bool recv, bool send)
    {
        //构造事件属性
        uint32_t event = 0;
        event |= (recv ? EVENT_IN : 0) | (send ? EVENT_OUT : 0);

        //写入内核
        _epoll_ptr->EpollerCtl(EPOLL_CTL_MOD, fd, event);
    }

    //读事件处理方法
    void Recver(std::weak_ptr<Connection> con)
    {
        //双重判断对象是否可用
        if(con.expired())
        {
            //已失效
            return;
        }
        auto connection = con.lock();
        //可用
        if(connection)
        {
            while(1)
            {
                //读取数据
                char buffer[size];
                int n = recv(connection->GetFd(), buffer, sizeof(buffer), 0);
                //读取成功
                if(n > 0)
                {
                    //添加到接收缓冲区
                    connection->AppendInbuffer(buffer);
                }
                //对方退出
                else if(n == 0)
                {
                    //按异常处理
                    connection->GetExceptCb()(connection);
                    return;
                }
                //读取失败
                else
                {
                    //非阻塞
                    if(errno == EWOULDBLOCK)
                    {
                        break;
                    }
                    //信号中断
                    else if(errno == EINTR)
                    {
                        continue;
                    }
                    //出错
                    else
                    {
                        lg(Error, "recv error");

                        //按异常处理
                        connection->GetExceptCb()(connection);
                        return;
                    }
                }
            }

            //上层业务处理
            _service(connection);
        }
        //不可用
        else
        {
            return;
        }
    }
    //写事件处理方法
    void Sender(std::weak_ptr<Connection> con)
    {
        //双重判断对象是否可用
        if(con.expired())
        {
            //对象已失效
            return;
        }
        auto connection = con.lock();
        if(connection)
        {
            //可用
            //获取发送缓冲区数据
            std::string& outbuffer = connection->GetOutBuffer();

            //不断send发送数据
            while(1)
            {
                int n = send(connection->GetFd(), outbuffer.c_str(), outbuffer.size(), 0);
                //发送成功
                if(n > 0)
                {
                    //删除已发送的数据
                    outbuffer.erase(0, n);

                    //发完就退出循环
                    if(outbuffer.empty())
                    {
                        break;
                    }
                }
                //发送失败
                else
                {   
                    //非阻塞
                    if(errno == EWOULDBLOCK)
                    {
                        break;
                    }
                    //信号中断
                    else if(errno == EINTR)
                    {
                        continue;
                    }
                    //出错
                    else
                    {
                        lg(Error, "send error");

                        //按异常事件处理
                        connection->GetExceptCb()(connection);
                        return;
                    }
                }
            }

            //send结束后判断发送缓冲区
            if(outbuffer.empty())
            {
                //空
                //关闭写事件的关心
                EnableEvent(connection->GetFd(), true, false);
            }
            else
            {
                //不为空
                //开启写事件的关心
                EnableEvent(connection->GetFd(), true, true);
            }
        }
        else
        {
            //不可用
            return;
        }
    }
    //异常事件处理方法
    void Excepter(std::weak_ptr<Connection> con)
    {
        //双重判断对象是否可用
        if(con.expired())
        {
            return;
        }
        auto connection = con.lock();
        if(connection)
        {
            //可用
            lg(Warning, "except, fd: %d, ip: %s, port: %d", connection->GetFd(), connection->GetIp(), connection->GetPort());
        
            //删除在内核的信息
            _epoll_ptr->EpollerCtl(EPOLL_CTL_DEL, connection->GetFd(), 0);

            //关闭fd
            close(connection->GetFd());

            //删除映射信息
            _connections.erase(connection->GetFd());
        }
        else
        {
            //不可用
            return;
        }
    }
    //获取连接(当作读事件处理方法，专门给监听套接字使用)
    void Accepter(std::weak_ptr<Connection> con)
    {
        //判断对象是否可用，双判断保险，如果一开始就不能用那么expierd可以提高效率，expierd比lock轻
        if(con.expired())
        {
            return;
        }
        auto connection = con.lock();
        //可用
        if(connection)
        {
            while(1)
            {
                struct sockaddr_in peer;
                memset(&peer, 0, sizeof(peer));
                socklen_t len = sizeof(peer);
                //获取新连接：获取新fd, 获取对方ip端口
                int accept_fd = ::accept(connection->GetFd(), (struct sockaddr*)&peer, &len);
                //获取成功
                if(accept_fd > 0)
                {
                    //获取ip端口
                    uint16_t peer_port = ntohs(peer.sin_port);
                    char peer_ip[128];
                    inet_ntop(AF_INET, &(peer.sin_addr), peer_ip, sizeof(peer_ip));

                    //设置非阻塞
                    SetNonBlock(accept_fd);

                    //创建连接对象
                    AddConnection(accept_fd, EVENT_IN, 
                                  std::bind(&Server::Recver, this, std::placeholders::_1), 
                                  std::bind(&Server::Sender, this, std::placeholders::_1), 
                                  std::bind(&Server::Excepter, this, std::placeholders::_1), 
                                  peer_ip, peer_port);
                }
                //获取失败
                else
                {
                    //非阻塞的等待超时
                    if(errno == EWOULDBLOCK)
                    {
                        break;
                    }
                    //被信号中断
                    else if(errno == EINTR)
                    {
                        continue;
                    }
                    //出错
                    else
                    {
                        break;
                    }
                }
            }
        }
        //不可用
        else
        {
            //...
            return;
        }
    }

    //创建连接对象
    void AddConnection(int fd, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb, std::string client_ip, uint16_t client_port)
    {
        //创建连接对象
        std::shared_ptr<Connection> connection(new Connection(fd));

        //获取服务器指针
        connection->SetWeakPtr(shared_from_this());
        //设置事件处理方法
        connection->SetHandler(recv_cb, send_cb, except_cb);
        //设置ip端口
        connection->SetIpPort(client_ip, client_port);

        //添加映射
        _connections.insert(std::make_pair(fd, connection));

        //把fd和关心事件交给内核
        _epoll_ptr->EpollerCtl(EPOLL_CTL_ADD, fd, event);
    }

    //通过fd判断连接对象是否存在
    bool ConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if(iter == _connections.end())
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    //获取就绪事件并处理
    void Dispatcher(int timeout)
    {
        //获取就绪队列
        int n = _epoll_ptr->EpollerWait(revents, num, timeout);

        for(int i=0; i<n; i++)
        {
            //获取事件和fd
            uint32_t event = revents[n].events;
            int fd = revents[n].data.fd;

            //判断是读事件
            if((event & EPOLLIN) && ConnectionSafe(fd))
            {
                if(_connections[fd]->GetRecvCb())
                {
                    _connections[fd]->GetRecvCb()(_connections[fd]);
                }
            }

            //判断是写事件
            if((event & EPOLLOUT) && ConnectionSafe(fd))
            {
                if(_connections[fd]->GetSendCb())
                {
                    _connections[fd]->GetSendCb()(_connections[fd]);
                }
            }
        }
    }
public:
    //构造
    Server(uint16_t port, func_t service)
    : _port(port)
    , _service(service)
    , _quit(true)
    , _epoll_ptr(new Epoller())
    , _listensock_ptr(new Sock())
    {}

    //服务器初始化
    void Init()
    {
        //监听套接字创建
        _listensock_ptr->Socket();
        //给fd设置非阻塞
        int n = SetNonBlock(_listensock_ptr->Fd());
        if(n == -1)
        {
            exit(0);
        }
        //绑定
        _listensock_ptr->Bind(_port);
        //监听
        _listensock_ptr->Listen();

        //创建连接对象: 每一个fd有一个对应的连接对象，连接对象负责描述管理fd
        AddConnection(_listensock_ptr->Fd(), EVENT_IN, 
                      std::bind(&Server::Accepter, this, std::placeholders::_1), nullptr, nullptr,
                      "0.0.0.0", 8080);
    }

    //启动
    void Loop()
    {
        //关闭退出标记
        _quit = false;

        while(1)
        {
            //事件派发
            Dispatcher(1000);
        }

        //打开退出标记
        _quit = true;
    }

private:
    std::shared_ptr<Epoller> _epoll_ptr; //epoll模型
    struct epoll_event revents[num]; //就绪队列

    std::shared_ptr<Sock> _listensock_ptr; //套接字
    std::unordered_map<int, std::shared_ptr<Connection>> _connections; //fd与连接对象映射表

    uint16_t _port; //端口号
    bool _quit; //服务器退出标记

    func_t _service; //业务
};

